﻿/*
 * Created by SharpDevelop.
 * User: Corrado Mio
 * Date: 13/06/2012
 * Time: 09:38
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Reflection;
using System.Runtime.Remoting;
using System.Text;

using CSharpExt.Reflection;

namespace CSharpExt.Data
{
    public class DbTypeException : DbException
    {
        #region Constructor
        
        public DbTypeException(String dbtype)
            : base(dbtype)
        {
            
        }
        
        #endregion
    }
    
    /// <summary>
    /// Crea un oggetto per accedere ad un database.
    /// I database supportati sono:
    /// 
    /// - sqlserver
    /// - oracle
    /// - msoracle: usa il provider Oracle di Microsoft
    /// - mysql     
    /// 
    /// E' possibile indicare il tipo del database direttamente nel
    /// dizionario dei parametri, con il parametro
    /// 
    /// - "DbType"
    /// 
    /// E' possibile indicare quale classe "Connection" istanziare, 
    /// indicando il nome completo della classe con il parametro:
    /// 
    /// - "ConnectionType"
    /// 
    /// </summary>
    public class DbConnectionFactory
    {
        #region Private Data
        
        private static Dictionary<String, String> CONNECTION_TYPE = new Dictionary<String, String>()
        {
            {"sqlserver", "System.Data.SqlClient.SqlConnection"}, 
            {"mysql",    "MySql.Data.MySqlClient.MySqlConnection"}, 
            {"msoracle", "System.Data.OracleClient.OracleConnection"}, 
            {"oracle",   "Oracle.DataAccess.Client.OracleConnection"}, 
        };
        
        #endregion
            
        #region Operations
        
        /// <summary>
        /// Crea una connessione verso un database. Non la apre
        /// </summary>
        /// <param name="dbtype">Tipo del database. Puo' essere null</param>
        /// <param name="args">Argomenti utilizzati per costruire la connection string</param>
        /// <returns></returns>
        public static DbConnection Create(String dbtype, Dictionary<String, String> args)
        {
            String connectionType = null;
            
            if (String.IsNullOrEmpty(dbtype) && !args.ContainsKey("DbType") && !args.ContainsKey("ConnectionType"))
                dbtype = "sqlserver";
            
            if (args.ContainsKey("DbType"))
                dbtype = args["DbType"];
            
            if (args.ContainsKey("ConnectionType"))
            {
                dbtype = "generic";
                connectionType = args["ConnectionType"];
            }
            
            if (connectionType == null && !CONNECTION_TYPE.ContainsKey(dbtype))
                throw new DbTypeException(dbtype);
            
            if (connectionType == null)
                connectionType = CONNECTION_TYPE[dbtype];
            
            return createConnection(connectionType, args);
        }

        /// <summary>
        /// Apre una connessione verso un database
        /// </summary>
        /// <param name="dbtype"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public static DbConnection Open(String dbtype, Dictionary<String, String> args)
        {
            DbConnection conn = Create(dbtype, args);
            
            conn.Open();

            return conn;
        }
        
        // ------------------------------------------------------------------
        // Create(args)
        // Open(args)
        // ------------------------------------------------------------------
        
        public static DbConnection Create(Dictionary<String, String> args)
        {
            return Create(null, args);
        }

        public static DbConnection Open(Dictionary<String, String> args)
        {
            DbConnection conn = Create(args);
            
            conn.Open();

            return conn;
        }
        
        public static DbConnectionStringBuilder Compose(Dictionary<String, String> args)
        {
            DbConnectionStringBuilder csb = new DbConnectionStringBuilder();
            foreach(KeyValuePair<String, String> arg in args)
            {
                if ("DbType".Equals(arg.Key))
                    continue;
                
                if ("ConnectionType".Equals(arg.Key))
                    continue;
                
                csb[arg.Key] = arg.Value;
            }
            return csb;
        }

        #endregion

        #region Implementation

        // ------------------------------------------------------------------
        // Support
        // ------------------------------------------------------------------
        
        private static DbConnection createConnection(String connectionType, Dictionary<String, String> args)
        {
            String connectionString = Compose(args).ConnectionString;
            
            DbConnection conn = TypeActivator.CreateInstance<DbConnection>(connectionType, new Object[]{connectionString});
            
            return conn;
        }
        
        #endregion
    }
}
